Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[meta] Getter interface for opaque collection types #1405

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

jpeletier
Copy link
Contributor

@jpeletier jpeletier commented Oct 17, 2024

Note: Must merge #1403 first

As a continuation to #1403, this PR adds getter opaque support for collection types.
One can now differentiate between reading and writing operations with a cursor, by means of setting a get_member or get_element callback:

  struct MyOpaque {
    int x;
    bool x_has_value = false;
    int y;
    bool y_has_value = false;
  };

  auto my_component = ecs.component<MyOpaque>();
  my_component.opaque(ecs.component().member<int>("a").member<int>("b"))
      .ensure_member([](MyOpaque *data, const char *member) -> void * {
        if (!strcmp(member, "a")) {
          data->x_has_value = true;
          return &data->x;
        } else if (!strcmp(member, "b")) {
          data->y_has_value = true;
          return &data->y;
        }
        return nullptr;
      })
      .get_member([](const MyOpaque *data, const char *member) -> const void * {
        if (!strcmp(member, "a") && data->x_has_value) {
          return &data->x;
        } else if (!strcmp(member, "b") && data->y_has_value) {
          return &data->y;
        }
        return nullptr;
      })
      .serialize([](const flecs::serializer *s, const MyOpaque *data) {
        if (data->x_has_value) {
          s->member("a");
          s->value(data->x);
        }
        if (data->y_has_value) {
          s->member("b");
          s->value(data->y);
        }

        return 0;
      });

  MyOpaque instance;

  flecs::cursor cur(ecs, my_component, &instance);

  cur.push();
  cur.member("a");
  cur.set_int(79);
  std::cout << "a = " << cur.get_int() << std::endl;  // value = 79
  cur.member("b");
  if (cur.get_read_ptr()) {
    std::cout << "b = " << cur.get_int() << std::endl;
  } else {
    std::cout << "b has no value" << std::endl;
  }

  std::cout << ecs.to_json(&instance) << std::endl;
  // output:
  // a = 79
  // b has no value
  // {"a":79}

With collections, one can now do the following. The below implements a pseudo sparse array opaque that is disguised to Flecs as a vector of floats:

  using MyMap = std::map<size_t, float>;

  auto float_vector = ecs.vector(flecs::F32);

  auto my_component = ecs.component<MyMap>();
  my_component.opaque(float_vector)
      .count([](const MyMap *m) -> size_t { return 100; })
      .resize([](MyMap *m, size_t size) {})
      .ensure_element([](MyMap *m, size_t elem) -> void * { return &(*m)[elem]; })
      .get_element([](const MyMap *m, size_t elem) -> const void * {
        if (auto it = m->find(elem); it != m->end()) {
          return &it->second;
        } else {
          return nullptr;
        }
      })
      .serialize([](const flecs::serializer *s, const MyMap *m) {
        if (m->empty()) {
          return 0;
        }
        size_t startIndex = 0;
        size_t prevKey = startIndex;
        auto it = m->begin();
        for (size_t i = startIndex; i < it->first; ++i) {
          s->value(0.0);
        }
        s->value(it->second);
        prevKey = it->first;
        for (++it; it != m->end(); ++it) {
          size_t currentKey = it->first;
          for (size_t gapKey = prevKey + 1; gapKey < currentKey; ++gapKey) {
            s->value(0.0);
          }
          s->value(it->second);
          prevKey = currentKey;
        }
        return 0;
      });

  MyMap instance;

  flecs::cursor cur(ecs, my_component, &instance);

  cur.push();
  cur.elem(2);
  cur.set_float(2.25);
  cur.elem(7);
  cur.set_float(7.25);
  cur.elem(10);
  cur.set_float(10.25);

  // try to access non-existing value:
  cur.elem(15); 
  if (!cur.get_read_ptr()) {
    std::cout << "Element #15 does not exist" << std::endl;
  } else {
    std::cout << "Element #15 = " << cur.get_float() << std::endl;
  }
  cur.pop();

  std::cout << ecs.to_json(&instance) << std::endl;
// output:
// Element #15 does not exist
// [0, 0, 2.25, 0, 0, 0, 0, 7.25, 0, 0, 10.25]

@jpeletier jpeletier force-pushed the getter-interface-collections branch from f4ed74e to d7ade02 Compare December 18, 2024 20:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant